#ifndef ALFS_BUNDLED_LIB_UNIQUE_PTR_HPP
#define ALFS_BUNDLED_LIB_UNIQUE_PTR_HPP
#include    <alfs/config.hpp>

// File [alfs/bundled_libs/unique_ptr/unique_ptr.hpp].
//
// Class hh::unique_ptr<>.
// An emulation of C++0x std::unique_ptr.
//
// (c) Copyright Alf P. Steinbach 2008.
// Original author Howard Hinnant, who placed this code in the public domain.
//
// Use, modification and distribution are subject to the "New BSD License"
// as listed at <url: http://www.opensource.org/licenses/bsd-license.php>.
//
// A not-quite-but-perhaps-good-enough partial implementation of C++0x unique_ptr
// for pre-C++0x compilers (that is, compilers without rvalue reference support),
// based on Howard Hinnant's public domain unique_ptr emulation at
// <url: http://home.twcny.rr.com/hinnant/cpp_extensions/unique_ptr_03.html>.
// Main changes have been to delete code that already exists in other headers,
// and replace references to Boost library with references to std namespace,
// plus disabling MSVC warnings (which, unbelievably, caused compilation error).
//
// Known problems: MSVC 7.1 has a tendency to choke on this code when other
// code generates warnings (possibly due to multiple copy constructors).

#include    <memory>
#include    <alfs/stdx/cppversion.hpp>      // IS_CPP0X_OR_LATER

//#if !ALFS_IS_CPP0X_OR_LATER && !defined( ALFS_UNIQUE_PTR_EMULATION_OFF )
#include <alfs/stdx/default_delete.hpp>  // std::default_delete
#include <alfs/stdx/type_traits.hpp>     // std::is_xxx etc.
#include <alfs/stdx/static_assert.hpp>   // static_assert emulation
#include <boost/compressed_pair.hpp>
#include <boost/static_assert.hpp>
#include <boost/mpl/if.hpp>
#include <boost/utility/enable_if.hpp>
#include <cstddef>

#ifdef   _MSC_VER
#    pragma  warning( push )
#    pragma  warning( disable: 4181 )        // Qualifier applied to reference type
#    pragma  warning( disable: 4521 )        // Multiple copy constructors
#    pragma  warning( disable: 4522 )        // Multiple assignment operators
#endif

namespace alfs{ namespace hh{

    namespace detail_unique_ptr {

        struct two {char _[2];};

        namespace pointer_type_imp
        {
            template <class U> static two  test(...);
            template <class U> static char test(typename U::pointer* = 0);
        }  // pointer_type_imp

        template <class T>
        struct has_pointer_type
        {
            static const bool value = sizeof(pointer_type_imp::test<T>(0)) == 1;
        };

        namespace pointer_type_imp
        {
            template <class T, class D, bool = has_pointer_type<D>::value>
            struct pointer_type
            {
                typedef typename D::pointer type;
            };

            template <class T, class D>
            struct pointer_type<T, D, false>
            {
                typedef T* type;
            };
        }  // pointer_type_imp

        template <class T, class D>
        struct pointer_type
        {
            typedef typename pointer_type_imp::pointer_type<T,
                typename std::remove_reference<D>::type>::type type;
        };

    }  // detail_unique_ptr

    template <class T, class D> class unique_ptr;

    namespace detail_unique_ptr
    {
        template <class T> struct unique_ptr_error;

        template <class T, class D>
        struct unique_ptr_error<const unique_ptr<T, D> >
        {
            typedef unique_ptr<T, D> type;
        };
    }  // detail_unique_ptr

    template <class T, class D = std::default_delete<T> >
    class unique_ptr
    {
        struct nat {int for_bool_;};
        typedef typename std::add_reference<D>::type deleter_reference;
        typedef typename std::add_reference<const D>::type deleter_const_reference;
    public:
        typedef T element_type;
        typedef D deleter_type;
        typedef typename detail_unique_ptr::pointer_type<T, D>::type pointer;

        // constructors
        unique_ptr() : ptr_(pointer())
        {
            static_assert(!std::is_pointer<D>::value, "");
        }
        explicit unique_ptr(pointer p) : ptr_(p)
        {
            static_assert(!std::is_pointer<D>::value, "");
        }
        unique_ptr(pointer p, typename boost::mpl::if_<
                            std::is_reference<D>,
                            D,
                            typename std::add_reference<const D>::type>::type d)
            : ptr_(p, d) {}
        unique_ptr(const unique_ptr& u)
            : ptr_(const_cast<unique_ptr&>(u).release(), u.get_deleter()) {}
        template <class U, class E>
            unique_ptr(const unique_ptr<U, E>& u,
                typename boost::enable_if_c<
                    std::is_convertible<typename unique_ptr<U, E>::pointer, pointer>::value &&
                    std::is_convertible<E, D>::value &&
                    (
                        !std::is_reference<D>::value ||
                        std::is_same<D, E>::value
                    )
                    ,
                    nat
                    >::type = nat())
                : ptr_(const_cast<unique_ptr<U,E>&>(u).release(), u.get_deleter()) {}

        // destructor
        ~unique_ptr() {reset();}

        // assignment
        unique_ptr& operator=(const unique_ptr& cu)
    {
            unique_ptr& u = const_cast<unique_ptr&>(cu);
            reset(u.release());
            ptr_.second() = u.get_deleter();
            return *this;
        }
        template <class U, class E>
        unique_ptr& operator=(const unique_ptr<U, E>& cu)
        {
            unique_ptr<U, E>& u = const_cast<unique_ptr<U, E>&>(cu);
            reset(u.release());
            ptr_.second() = u.get_deleter();
            return *this;
        }
        unique_ptr& operator=(int nat::*)
        {
            reset();
            return *this;
        }

        // observers
        typename std::add_reference<T>::type operator*()  const {return *ptr_.first();}
        pointer operator->() const {return ptr_.first();}
        pointer get()        const {return ptr_.first();}
        deleter_reference       get_deleter()       {return ptr_.second();}
        deleter_const_reference get_deleter() const {return ptr_.second();}
        operator int nat::*() const {return ptr_.first() ? &nat::for_bool_ : 0;}

        // modifiers
        pointer release()
        {
            pointer tmp = ptr_.first();
            ptr_.first() = pointer();
            return tmp;
        }
        void reset(pointer p = pointer())
        {
            if (ptr_.first() != p)
            {
                if (ptr_.first())
                    ptr_.second()(ptr_.first());
                ptr_.first() = p;
            }
        }
        void swap(unique_ptr& u) {ptr_.swap(u.ptr_);}
    private:
        boost::compressed_pair<pointer, D> ptr_;

        unique_ptr(unique_ptr&);
        template <class U, class E> unique_ptr(unique_ptr<U, E>&);
        template <class U> unique_ptr(U&, typename detail_unique_ptr::unique_ptr_error<U>::type = 0);

        
        unique_ptr& operator=(unique_ptr&);
        template <class U, class E> unique_ptr& operator=(unique_ptr<U, E>&);
        template <class U> typename detail_unique_ptr::unique_ptr_error<U>::type operator=(U&);
    };

    template <class T, class D>
    class unique_ptr<T[], D>
    {
        struct nat {int for_bool_;};
        typedef typename std::add_reference<D>::type deleter_reference;
        typedef typename std::add_reference<const D>::type deleter_const_reference;
    public:
        typedef T element_type;
        typedef D deleter_type;
        typedef typename detail_unique_ptr::pointer_type<T, D>::type pointer;

        // constructors
        unique_ptr() : ptr_(pointer())
        {
            static_assert(!std::is_pointer<D>::value, "");
        }
        explicit unique_ptr(pointer p) : ptr_(p)
        {
            static_assert(!std::is_pointer<D>::value, "");
        }
        unique_ptr(pointer p, typename boost::mpl::if_<
                            std::is_reference<D>,
                            D,
                            typename std::add_reference<const D>::type>::type d)
            : ptr_(p, d) {}
        unique_ptr(const unique_ptr& u)
            : ptr_(const_cast<unique_ptr&>(u).release(), u.get_deleter()) {}

        // destructor
        ~unique_ptr() {reset();}

        // assignment
        unique_ptr& operator=(const unique_ptr& cu)
        {
            unique_ptr& u = const_cast<unique_ptr&>(cu);
            reset(u.release());
            ptr_.second() = u.get_deleter();
            return *this;
        }
        unique_ptr& operator=(int nat::*)
        {
            reset();
            return *this;
        }

        // observers
        typename std::add_reference<T>::type operator[](std::size_t i)  const {return ptr_.first()[i];}
        pointer                 get() const         {return ptr_.first();}
        deleter_reference       get_deleter()       {return ptr_.second();}
        deleter_const_reference get_deleter() const {return ptr_.second();}
        operator int nat::*() const {return ptr_.first() ? &nat::for_bool_ : 0;}

        // modifiers
        pointer release()
        {
            pointer tmp = ptr_.first();
            ptr_.first() = pointer();
            return tmp;
        }
        void reset(pointer p = pointer())
        {
            if (ptr_.first() != p)
            {
                if (ptr_.first())
                    ptr_.second()(ptr_.first());
                ptr_.first() = p;
            }
        }
        void swap(unique_ptr& u) {ptr_.swap(u.ptr_);}

    private:
        boost::compressed_pair<pointer, D> ptr_;

        template <class U, class E> unique_ptr(U p, E,
            typename boost::enable_if<std::is_convertible<U, pointer> >::type* = 0);
        template <class U> explicit unique_ptr(U,
            typename boost::enable_if<std::is_convertible<U, pointer> >::type* = 0);

        unique_ptr(unique_ptr&);
        template <class U> unique_ptr(U&, typename detail_unique_ptr::unique_ptr_error<U>::type = 0);

        unique_ptr& operator=(unique_ptr&);
        template <class U> typename detail_unique_ptr::unique_ptr_error<U>::type operator=(U&);
    };

    template <class T, class D, std::size_t N>
    class unique_ptr<T[N], D>
    {
        struct nat {int for_bool_;};
        typedef typename std::add_reference<D>::type deleter_reference;
        typedef typename std::add_reference<const D>::type deleter_const_reference;
    public:
        typedef T element_type;
        typedef D deleter_type;
        typedef typename detail_unique_ptr::pointer_type<T, D>::type pointer;
        static const std::size_t size = N;

        // constructors
        unique_ptr() : ptr_(pointer())
        {
            static_assert(!std::is_pointer<D>::value, "");
        }
        explicit unique_ptr(pointer p) : ptr_(p)
        {
            static_assert(!std::is_pointer<D>::value, "");
        }
        unique_ptr(pointer p, typename boost::mpl::if_<
                            std::is_reference<D>,
                            D,
                            typename std::add_reference<const D>::type>::type d)
            : ptr_(p, d) {}
        unique_ptr(const unique_ptr& u)
            : ptr_(const_cast<unique_ptr&>(u).release(), u.get_deleter()) {}

        // destructor
        ~unique_ptr() {reset();}

        // assignment
        unique_ptr& operator=(const unique_ptr& cu)
        {
            unique_ptr& u = const_cast<unique_ptr&>(cu);
            reset(u.release());
            ptr_.second() = u.get_deleter();
            return *this;
        }
        unique_ptr& operator=(int nat::*)
        {
            reset();
            return *this;
        }

        // observers
        typename std::add_reference<T>::type operator[](std::size_t i)  const {return ptr_.first()[i];}
        pointer                 get() const         {return ptr_.first();}
        deleter_reference       get_deleter()       {return ptr_.second();}
        deleter_const_reference get_deleter() const {return ptr_.second();}
        operator int nat::*() const {return ptr_.first() ? &nat::for_bool_ : 0;}

        // modifiers
        pointer release()
        {
            pointer tmp = ptr_.first();
            ptr_.first() = pointer();
            return tmp;
        }
        void reset(pointer p = pointer())
        {
            if (ptr_.first() != p)
            {
                if (ptr_.first())
                    ptr_.second()(ptr_.first(), N);
                ptr_.first() = p;
            }
        }
        void swap(unique_ptr& u) {ptr_.swap(u.ptr_);}
    private:
        boost::compressed_pair<pointer, D> ptr_;

        template <class U, class E> unique_ptr(U p, E,
            typename boost::enable_if<std::is_convertible<U, pointer> >::type* = 0);
        template <class U> explicit unique_ptr(U,
            typename boost::enable_if<std::is_convertible<U, pointer> >::type* = 0);

        unique_ptr(unique_ptr&);
        template <class U> unique_ptr(U&, typename detail_unique_ptr::unique_ptr_error<U>::type = 0);

        unique_ptr& operator=(unique_ptr&);
        template <class U> typename detail_unique_ptr::unique_ptr_error<U>::type operator=(U&);
    };

    template <class T, class D>
    inline
    void swap(unique_ptr<T, D>& x, unique_ptr<T, D>& y) {x.swap(y);}

    template <class T1, class D1, class T2, class D2>
    inline
    bool
    operator==(const unique_ptr<T1, D1>& x, const unique_ptr<T2, D2>& y)
        {return x.get() == y.get();}

    template <class T1, class D1, class T2, class D2>
    inline
    bool
    operator!=(const unique_ptr<T1, D1>& x, const unique_ptr<T2, D2>& y)
        {return x.get() != y.get();}

    template <class T1, class D1, class T2, class D2>
    inline
    bool
    operator <(const unique_ptr<T1, D1>& x, const unique_ptr<T2, D2>& y)
        {return x.get() < y.get();}

    template <class T1, class D1, class T2, class D2>
    inline
    bool
    operator<=(const unique_ptr<T1, D1>& x, const unique_ptr<T2, D2>& y)
        {return x.get() <= y.get();}

    template <class T1, class D1, class T2, class D2>
    inline
    bool
    operator >(const unique_ptr<T1, D1>& x, const unique_ptr<T2, D2>& y)
        {return x.get() > y.get();}

    template <class T1, class D1, class T2, class D2>
    inline
    bool
    operator>=(const unique_ptr<T1, D1>& x, const unique_ptr<T2, D2>& y)
        {return x.get() >= y.get();}

    template <class T, class D>
    inline
    unique_ptr<T, D>
    move(unique_ptr<T, D>& p)
    {
        return unique_ptr<T, D>(p.release(), p.get_deleter());
    }

} }  // alfs::hh

#ifdef   _MSC_VER
#    pragma  warning( pop )
#endif

#endif
